#include "utility.h"

#include <fstream>
#include <algorithm>
#include <sstream>
#include <random>
#include <base64/base64.h>

namespace utility {
  bool read_file(const std::string& file, std::string *content) {
    if (!content) return false;
    std::ifstream in(file, std::ios::binary);
    if (!in.is_open()) return false;
    in.seekg(0, std::ios::end);
    auto len = in.tellg();
    in.seekg(0, std::ios::beg);
    content->resize(len);
    in.read((char*)content->data(), len);
    in.close();
    return true;
  }
  bool write_file(const std::string& file, const char *data, size_t len) {
    if (len <= 0) return false;
    std::ofstream out(file, std::ios::binary);
    if (!out.is_open()) return false;
    out.write(data, len);
    out.close();
    return true;
  }
  bool json_read_file(const std::string& file, crow::json::rvalue *json) {
    std::string content;
    if (!read_file(file, &content))
      return false;
    *json = crow::json::load(content.data(), content.size());
    return true;
  }
  bool json_write_file(const std::string& file, const crow::json::wvalue &json) {
    auto content = crow::json::dump(json);
    return write_file(file, content.data(), content.size());
  }
  
  crow::response response_file(const std::string& file, ResponseFileType type) {
    std::string content;
    if (!read_file(file, &content))
      return crow::response(404);
    
    auto ext = file_extension(file);
    if (type == ResponseFileType::Null)
      type = guess_response_file_type_with_extension(ext);
    
    auto resp = crow::response(content);
    switch (type) {
      case ResponseFileType::Text:
        resp.add_header("Content-Type", "text/html; charset=utf-8");
        break;
      case ResponseFileType::Json:
        resp.add_header("Content-Type", "text/javascript");
        break;
      case ResponseFileType::Css:
        resp.add_header("Content-Type", "text/css");
        break;
      case ResponseFileType::Image:
        if (ext == "svg") resp.add_header("Content-Type", "image/" + ext + "+xml");
        else resp.add_header("Content-Type", "image/" + ext);
        break;
    }
    resp.add_header("Content-Length", std::to_string(resp.body.size()));

    return std::move(resp);
  }
  ResponseFileType guess_response_file_type_with_extension(const std::string& ext) {
    if (ext == "js") return ResponseFileType::Json;
    else if (ext == "css") return ResponseFileType::Css;
    // images
    else if (ext == "svg") return ResponseFileType::Image;
    else if (ext == "png") return ResponseFileType::Image;
    else if (ext == "jpg") return ResponseFileType::Image;
    else if (ext == "bmp") return ResponseFileType::Image;
    else if (ext == "gif") return ResponseFileType::Image;
    else if (ext == "webp") return ResponseFileType::Image;
    else if (ext == "apng") return ResponseFileType::Image;

    else return ResponseFileType::Text;
  }

  bool str_start_with(const char *src, const char *str, size_t len) {
    if (!src || !str) return false;
    if (len <= 0) len = strlen(str);
    for (int i = 0; i < len; ++i) {
      if (src[i] != str[i])
        return false;
    }
    return true;
  }
  bool str_end_with(const char *src, const char *str, size_t len) {
    if (!src || !str) return false;
    if (len <= 0) len = strlen(str);
    for (int i = len - 1; i >= 0; --i) {
      if (src[i] != str[i])
        return false;
    }
    return true;
  }

  std::string file_extension(const std::string &file) {
    std::string ext = file.substr(file.rfind(".") + 1);
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
    return std::move(ext);
  }
  std::string file_path(const std::string &file) {
    std::string path = file.substr(0, file.rfind("/")+1);
    return std::move(path);
  }

  std::string generate_random_hex(const unsigned int len) {
    static const char dict[] = "0123456789ABCDEF";
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 0xf);

    std::stringstream ss;
    for (auto i = 0; i < len; i++) {
      unsigned int rc = dis(gen);
      ss << dict[rc];
    }
    return ss.str();
  }

  std::string base64_encode(const std::string &str) {
    auto len = base64_needed_encoded_length(str.length());
    std::string ret;
    ret.resize(len);
    ::base64_encode(str.data(), str.length(), (char*)ret.data());
    return std::move(ret);
  }
  std::string base64_decode(const std::string &str) {
    int len = base64_needed_decoded_length(str.length());
    std::string ret;
    ret.resize(len);
    auto l = ::base64_decode(str.data(), str.length(), (char*)ret.data(), nullptr);
    if (l != len && l >= 0) ret.resize(l);
    return std::move(ret);
  }

  eHttpMethod get_method_type(const std::string &str) {
    std::string mtd = str;
    std::transform(mtd.begin(), mtd.end(), mtd.begin(), ::tolower);
    if (mtd == "get") {
      return eHttpMethod::Get;
    } else if (mtd == "post") {
      return eHttpMethod::Post;
    } else if (mtd == "delete") {
      return eHttpMethod::Delete;
    } else if (mtd == "put") {
      return eHttpMethod::Put;
    }
    return eHttpMethod::Null;
  }
  eApiType get_api_type(const std::string &str) {
    std::string mtd = str;
    std::transform(mtd.begin(), mtd.end(), mtd.begin(), ::tolower);
    if (mtd == "Number") {
      return eApiType::Number;
    } else if (mtd == "Float") {
      return eApiType::Float;
    } else if (mtd == "String") {
      return eApiType::String;
    } else if (mtd == "Path") {
      return eApiType::Path;
    }
    return eApiType::Simple;
  }
}